package cn.xinfei.xdecision.engine.runner.api;

import cn.xinfei.xdecision.common.datamodel.DecisionResult;
import cn.xinfei.xdecision.constant.EngineResultEnum;
import cn.xinfei.xdecision.constant.GlobalConstant;
import cn.xinfei.xdecision.engine.runner.context.BranchTypeEnum;
import cn.xinfei.xdecision.engine.runner.context.PipelineContextHolder;
import cn.xinfei.xdecision.engine.runner.context.XDecisionContext;
import cn.xinfei.xdecision.engine.runner.executor.NodeHandler;
import cn.xinfei.xdecision.engine.runner.metadata.MetadataProvider;
import cn.xinfei.xdecision.engine.runner.service.DecisionIdBuilder;
import cn.xinfei.xdecision.engine.runner.service.DecisionResultService;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.Date;
import java.util.Map;
import cn.xinfei.xdecision.common.utils.util.JsonUtil;

@Slf4j
public abstract class AbstractDecision<REQ, RES> {
    @Autowired
    private DecisionIdBuilder decisionIdBuilder;

    @Autowired
    private DecisionResultService decisionResultService;

    @Autowired
    private MetadataProvider metadataProvider;

    public void before() {
        try {
            String decisionId = PipelineContextHolder.getDecisionId();
            if (StringUtils.isEmpty(decisionId)) {
                String scene = PipelineContextHolder.getScene();
                decisionId = decisionIdBuilder.create(scene);
            }
            String bizId = PipelineContextHolder.getRequestId();
            if (StringUtils.isEmpty(bizId)) {
                bizId = decisionId;
            }
            MDC.put(GlobalConstant.TRACE_ID, bizId);
            MDC.put(GlobalConstant.PID, decisionId);
            String branch = PipelineContextHolder.getBranch();
            if (StringUtils.isEmpty(branch)) {
                branch = BranchTypeEnum.MAIN.name();
            }
            MDC.put(GlobalConstant.BRANCH, branch);
            String engineCode = PipelineContextHolder.getEngineCode();
            Map<String, NodeHandler> engineNodeMap = metadataProvider.getEngineNodeList(engineCode);
            XDecisionContext context = XDecisionContext.getContext();
            context.getPipelineMetaInfoContext().putAll(engineNodeMap);

            String mainDecisionId = PipelineContextHolder.getMainDecisionId();
            if(StringUtils.isEmpty(mainDecisionId)){
                mainDecisionId=decisionId;
            }
            PipelineContextHolder.setDecisionId(decisionId);
            PipelineContextHolder.setMainDecisionId(mainDecisionId);
            PipelineContextHolder.setRequestId(bizId);

        } catch (Exception e) {
            log.error("beforeRun fail.msg={}", e.getMessage(), e);
        }
    }


    public void beforeSave() throws Exception {
        log.info("before save....");
        after(0l);

    }
    public final <RES> RES execute(REQ REQ) throws Exception {
        long start = System.currentTimeMillis();
        before();
        beforeSave();
        RES res = run(REQ);
        long end = System.currentTimeMillis();
        long respond = end - start;
        after(respond);
        return res;
    }

    public abstract <RES> RES run(REQ REQ);


    public DecisionResult after(long respond) throws Exception {

        DecisionResult decisionResult = new DecisionResult();
        try {

            EngineResultEnum engineResultEnum = PipelineContextHolder.getEngineResult();
            String decisionId = PipelineContextHolder.getDecisionId();
            decisionResult.setDecisionId(decisionId);
            decisionResult.setMainDecisionId(PipelineContextHolder.getMainDecisionId());
            decisionResult.setScene(PipelineContextHolder.getScene());
            decisionResult.setCode(engineResultEnum.getCode());
            decisionResult.setResult(engineResultEnum.getResult());
            decisionResult.setState(engineResultEnum.getState());
            decisionResult.setRespond(respond);
            decisionResult.setEngineName(PipelineContextHolder.getEngineName());
            decisionResult.setEngineVersion(PipelineContextHolder.getEngineVersion());
            decisionResult.setEngineCode(PipelineContextHolder.getEngineCode());
            decisionResult.setBranch(PipelineContextHolder.getBranch());
            decisionResult.setOut(PipelineContextHolder.getOut());
            decisionResult.setUpdateTime(new Date());
            decisionResult.setParentDecisionId(PipelineContextHolder.getParentDecisionId());
            decisionResult.setChampionDecisionId(PipelineContextHolder.getChampionDecisionId());
            decisionResult.setSync(PipelineContextHolder.getSync());
            decisionResult.setTpCode(PipelineContextHolder.getTpCode());
            decisionResult.setRequestId(PipelineContextHolder.getRequestId());
            decisionResult.setOrganId(PipelineContextHolder.getOrganId());

            long start = System.currentTimeMillis();
            decisionResultService.upsertDecisionResultByDecisionId(decisionResult,decisionId,decisionId);
//            decisionResultService.saveDecisionResultByDecisionId(decisionResult);
            long end = System.currentTimeMillis();

            log.info("persistenceDuration={}", end - start);
        } catch (Exception e) {
            log.error("updateDecisionResultByDecisionId error={},decisionResult={}", e.getMessage(), JsonUtil.toJson(decisionResult), e);
            throw e;
        }
        return decisionResult;
    }


}
